/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */

using System;
using System.Text.RegularExpressions;
using Velocity4Net.Runtime.Parse;
using Velocity4Net.Runtime.Directives;
using Velocity4Net.Util.Introspection;
namespace Velocity4Net.Errors
{

    /**
     *  Application-level exception thrown when a resource of any type
     *  has a syntax or other error which prevents it from being parsed.
     *  <br>
     *  When this resource is thrown, a best effort will be made to have
     *  useful information in the exception's message.  For complete
     *  information, consult the runtime log.
     *
     * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
     * @author <a href="hps@intermeta.de">Henning P. Schmiedehausen</a>
     * @version $Id: ParseErrorException.java 736638 2009-01-22 13:42:52Z byron $
     */
    public class ParseErrorException : VelocityException
    {
        /**
         * The column number of the parsing error, or -1 if not defined.
         */
        private int columnNumber = -1;

        /**
         * The line number of the parsing error, or -1 if not defined.
         */
        private int lineNumber = -1;

        /**
         * The name of the template containing the error, or null if not defined.
         */
        private String templateName = "*unset*";

        /**
         * If applicable, contains the invalid syntax or reference that triggered this exception
         */
        private String invalidSyntax;

        /**
         * If we modify the message, then we set this
         */
        private String msg = null;

        /**
         * Create a ParseErrorException with the given message.
         *
         * @param exceptionMessage the error exception message
         */
        public ParseErrorException(String exceptionMessage)
            : base(exceptionMessage)
        {
        }

        private Regex lexError = new Regex("Lexical error.*TokenMgrError.*line (\\d+),.*column (\\d+)\\.(.*)");

        /**
         * Create a ParseErrorException with the given ParseException.
         *
         * @param pex the parsing exception
         * @since 1.5
         */
        public ParseErrorException(ParseException pex, String templName)
            : base(pex.Message)
        {

            if (templName != null) templateName = templName;

            // Don't use a second C'tor, TemplateParseException is a subclass of
            // ParseException...
            if (pex is ExtendedParseException)
            {
                ExtendedParseException xpex = (ExtendedParseException)pex;

                columnNumber = xpex.ColumnNumber;
                lineNumber = xpex.LineNumber;
                templateName = xpex.TemplateName;
            }
            else
            {
                // We get here if the the Parser has thrown an exception. Unfortunately,
                // the error message created is hard coded by javacc, so here we alter
                // the error message, so that it is in our standard format.          
                //Matcher match = lexError.matcher(pex.getMessage());
                //if (match.matches())
                //{
                //    lineNumber = Integer.parseInt(match.group(1));
                //    columnNumber = Integer.parseInt(match.group(2));
                //    String restOfMsg = match.group(3);
                //    msg = "Lexical error, " + restOfMsg + " at "
                //      + Log.formatFileString(templateName, lineNumber, columnNumber);
                //}

                ////  ugly, ugly, ugly...

                //if (pex.currentToken != null && pex.currentToken.next != null)
                //{
                //    columnNumber = pex.currentToken.next.beginColumn;
                //    lineNumber = pex.currentToken.next.beginLine;
                //}
            }
        }

        /**
         * Create a ParseErrorException with the given ParseException.
         *
         * @param pex the parsing exception
         * @since 1.5
         */
        public ParseErrorException(VelocityException pex, String templName)
            : base(pex.Message)
        {

            if (templName != null) templateName = templName;

            // Don't use a second C'tor, TemplateParseException is a subclass of
            // ParseException...
            if (pex is ExtendedParseException)
            {
                ExtendedParseException xpex = (ExtendedParseException)pex;

                columnNumber = xpex.ColumnNumber;
                lineNumber = xpex.LineNumber;
                templateName = xpex.TemplateName;
            }
            else if (pex.WrappedThrowable is ParseException)
            {
                ParseException pex2 = (ParseException)pex.WrappedThrowable;

                if (pex2.currentToken != null && pex2.currentToken.next != null)
                {
                    columnNumber = pex2.currentToken.next.beginColumn;
                    lineNumber = pex2.currentToken.next.beginLine;
                }
            }
        }


        /**
         * Create a ParseErrorRuntimeException with the given message and info
         * 
         * @param exceptionMessage the error exception message
         * @param info an Info object with the current template info
         * @since 1.5
         */
        public ParseErrorException(String exceptionMessage, Info info)
            : base(exceptionMessage)
        {
            columnNumber = info.getColumn();
            lineNumber = info.getLine();
            templateName = info.getTemplateName();
        }

        /**
         * Create a ParseErrorRuntimeException with the given message and info
         * 
         * @param exceptionMessage the error exception message
         * @param info an Info object with the current template info
         * @param invalidSyntax the invalid syntax or reference triggering this exception
         * @since 1.5
         */
        public ParseErrorException(String exceptionMessage,
                Info info, String invalidSyntax)
            : base(exceptionMessage)
        {
            columnNumber = info.getColumn();
            lineNumber = info.getLine();
            templateName = info.getTemplateName();
            this.invalidSyntax = invalidSyntax;
        }


        /**
         * Return the column number of the parsing error, or -1 if not defined.
         *
         * @return column number of the parsing error, or -1 if not defined
         * @since 1.5
         */
        public int ColumnNumber
        {
            get
            {
                return columnNumber;
            }
        }

        /**
         * Return the line number of the parsing error, or -1 if not defined.
         *
         * @return line number of the parsing error, or -1 if not defined
         * @since 1.5
         */
        public int LineNumber
        {
            get
            {
                return lineNumber;
            }
        }

        /**
         * Return the name of the template containing the error, or null if not
         * defined.
         *
         * @return the name of the template containing the parsing error, or null
         *      if not defined
         * @since 1.5
         */
        public String TemplateName
        {
            get
            {
                return templateName;
            }
        }

        /**
         * Return the invalid syntax or reference that triggered this error, or null
         * if not defined.
         * 
         * @return Return the invalid syntax or reference that triggered this error, or null
         * if not defined
         * @since 1.5
         */
        public String InvalidSyntax
        {
            get
            {
                return invalidSyntax;
            }
        }

        /**
         * Return our custum message if we have one, else return the default message
         */
        public String Message
        {
            get
            {
                if (msg != null) return msg;
                return base.Message;
            }
        }
    }
}